﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using GdPicture;
using LprWrapperSharp;
using MiscUtil;
using FontStyle = GdPicture.FontStyle;
using ImageFormat = LprWrapperSharp.ImageFormat;

namespace Kise.LprCapturer
{

    public class RecogResult
    {
        public LprWrapperSharp.RecognizeResult[] Result { get; set; }
        public Frame Frame { get; set; }
    }


    public class LprCapturer
    {
        private GdPicture.GdPictureImaging _gdPictureImaging;
        private GdPicture.LicenseManager _licenseManager;

        private readonly ConcurrentQueue<Frame> _frames = new ConcurrentQueue<Frame>();
        private readonly List<IFrameCapture> _frameCaptures = new List<IFrameCapture>();
        private static volatile LprWrapperSharp.LprRecognizer _recognizer;
        private ISpeedCalculator _speedCalculator;
        private object speedCalculatorLocker = new object();
        private ParameterReader _parameterReader;
        private bool _started;
        private volatile string _filePathFormatString;
        private volatile string _targetDir;
        private Thread _lprRecognizeThread;

        public event EventHandler<EventArgs<LprCaptured>> NewLprCaptured;
        public event EventHandler<EventArgs<H264Frame>> H264FrameReceived;


        public LaneConfig[] LaneConfigs { get; set; }


        public string FilePathFormatString
        {
            get
            {
                return _filePathFormatString;
            }
            set
            {
                _filePathFormatString = value;
            }
        }


       

        public string TagetDir
        {
            get { return _targetDir; }
            set { _targetDir = value; }
        }

        public void RaiseNewLprCaptured(EventArgs<LprCaptured> e)
        {
            EventHandler<EventArgs<LprCaptured>> handler = NewLprCaptured;
            if (handler != null) handler(this, e);
        }

        public void RaiseH264FrameReceived(EventArgs<H264Frame> e)
        {
            EventHandler<EventArgs<H264Frame>> handler = H264FrameReceived;
            if (handler != null) handler(this, e);
        }

        public string ProvienceCharacter
        {
            set
            {
                if (_recognizer != null && !string.IsNullOrEmpty(value))
                {
                    _recognizer.SetCharacterPriority(value);
                }
            }
        }

        public Rectangle[] Rois { get; set; } //识别区域

        public bool SaveImage { get; set; }

        public LprCapturer()
        {

            _lprRecognizeThread = new Thread(ProcessGroup);
            _lprRecognizeThread.IsBackground = true;
            _lprRecognizeThread.Start();


            LaneConfigs = new LaneConfig[0];

        }


        public bool LprCapturerEnabled
        {
            set
            {
                if (value)
                {

                    if (_recognizer == null)
                    {
                        _recognizer = new LprRecognizer();
                        _recognizer.SetImageFormat(ImageFormat.BGR, false, true);

                        _licenseManager = new LicenseManager();
                        var res = _licenseManager.RegisterKEY("4118819291818687169311706");

                        _gdPictureImaging = new GdPictureImaging();

                        _filePathFormatString = GenerateDefaultPathFormat();
                    }
                }
            }
        }


        public ISpeedCalculator SpeedCalculator
        {
            get
            {
                lock (speedCalculatorLocker)
                {
                    return _speedCalculator;
                }
            }
            set
            {
                lock (speedCalculatorLocker)
                {
                    _speedCalculator = value;
                }
            }
        }


        public void AddEndPoint(IPEndPoint endPoint)
        {
            var capturer = new TcpReceiver(endPoint);
            capturer.NewFrameCaptured += FrameCaptureOnNewFrameCaptured;
            capturer.Start();

            _frameCaptures.Add(capturer);
        }

        public void StopAllEndPoint()
        {
            foreach (var frameCapture in _frameCaptures)
            {
                frameCapture.NewFrameCaptured -= FrameCaptureOnNewFrameCaptured;
                frameCapture.Stop();
            }
        }


        private void ParameterReaderOnReadingCompleted(object sender, EventArgs<ParameterReading> eventArgs)
        {
            return;

            if (eventArgs.Value.Error == null)
            {
                var reading = eventArgs.Value;

                var calculator = new VideoSpeedCalculator(
                    reading.LeftTop.X,
                    reading.LeftTop.Y,
                    reading.RightTop.X,
                   reading.RightTop.Y,
                   reading.LeftBottom.X,
                reading.LeftBottom.Y,
                reading.RightBottom.X,
                reading.RightBottom.Y,
                reading.Width, reading.Height, 1.0f);

                SpeedCalculator = calculator;


                _parameterReader.ReadingCompleted -= ParameterReaderOnReadingCompleted;

            }
        }


        private void FrameCaptureOnNewFrameCaptured(object sender, EventArgs<Frame> eventArgs)
        {
            if (IsNewGroupBegin(eventArgs.Value))
            {
                System.Diagnostics.Debug.WriteLine("group count={0}", _frames.Count);
                //ProcessLastGroup();
            }

            _frames.Enqueue(eventArgs.Value);

            // ProcessGroup(eventArgs.Value);

        }

        private void ProcessGroup(object state)
        {
            try
            {
                while (true)
                {
                    Frame frame;
                    if (_frames.TryDequeue(out frame))
                    {
                        if (frame.H264 != null)
                        {
                            var h264 = frame.ToH264Frame();
                            try
                            {
                                this.RaiseH264FrameReceived(new EventArgs<H264Frame>(h264));
                            }
                            catch (Exception ex)
                            {
                                System.Diagnostics.Debug.WriteLine(ex);
                            }
                        }

                        if (_recognizer != null)
                        {
                            RecognizeLpr(frame);
                        }

                    }
                    else
                    {
                        Thread.Sleep(50);
                    }
                }
            }
            catch (Exception ex)
            {

                throw;
            }


        }

        private void RecognizeLpr(Frame frame)
        {
            if (frame.Images.Length == 0)
            {
                return;
            }

            var recogRes = new List<Tuple<int, RecognizeResult>>();
            foreach (var i in frame.Images)
            {
                try
                {
                    var ms = new MemoryStream(i.JpegBuffer);
                    var img = Image.FromStream(ms);
                    var r = RecognizeOneImage(img);
                    if (r.Succeed)
                    {
                        var laneNo = CalcLaneNo(img, r, frame);
                        recogRes.Add(new Tuple<int, RecognizeResult>(laneNo, r));
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex);
                }
            }


            var lprCaptured = new LprCaptured();
            lprCaptured.CaptureTime = frame.Images[0].CaptureTime;
            lprCaptured.LprNumber = recogRes.Count > 0 ? recogRes[0].Item2.PlateNumber : "未检测";
            lprCaptured.JpegBuffers = frame.Images.Select(ci => ci.JpegBuffer).ToList();
            lprCaptured.LocationId = frame.LocationId;
            lprCaptured.Speed = frame.Speed;
            lprCaptured.SpeedLimit = frame.SpeedLimit;
            lprCaptured.ViolationCode = frame.ViolationType;
            if (recogRes.Count > 0)
            {
                lprCaptured.RoadNo = recogRes[0].Item1;
            }
            

            try
            {
                PrintAndSaveImage(lprCaptured);
            }
            catch (System.IO.IOException ex)
            {
            }

            try
            {
                RaiseNewLprCaptured(new EventArgs<LprCaptured>(lprCaptured));
            }
            catch (Exception ex)
            {
            }
        }

        private int CalcLaneNo(Image img, RecognizeResult recogResult, Frame frame)
        {
            var plateRectangle = recogResult.PlateRectangle;
            var center = new Point(plateRectangle.Left + plateRectangle.Width/2,
                                   plateRectangle.Top + plateRectangle.Height/2);
            var imageLeftPart = new Rectangle(0, 0, img.Width/2, img.Height);
            var isInLeftPart = imageLeftPart.Contains(center);

            LaneConfig config;
            if (TryFindLaneConfig(frame.LocationId, out config))
            {
                return isInLeftPart ? config.LeftLaneId : config.RightLaneId;
            }

            return isInLeftPart ? 1 : 2;

        }

        private bool TryFindLaneConfig(string locationId, out LaneConfig config)
        {
            config = LaneConfigs.FirstOrDefault(l => System.String.Compare(locationId, l.CameraId, System.StringComparison.OrdinalIgnoreCase) == 0);
            return config != null;

        }

        //private void ProcessLastGroup()
        //{
        //    var res = _frames.Select(RecogniseOneFrame).ToList();
        //    var singlePicResults = new List<RecogResult>();
        //    if (res.Count == 2)
        //    {
        //        var plateNumbers1 = res[0];
        //        var plateNumbers2 = res[1];

        //        //计算车速，在两帧图片中都出现的车牌才能计算
        //        var commonPlates = plateNumbers1.Result.Where(r => r.Succeed).Intersect(plateNumbers2.Result.Where(r => r.Succeed), new RecogResultComparer()).ToList();
        //        foreach (var r in commonPlates)
        //        {
        //            var r1 = plateNumbers1.Result.First(p => p.PlateNumber == r.PlateNumber);
        //            var r2 = plateNumbers2.Result.First(p => p.PlateNumber == r.PlateNumber);

        //            var x1 = r1.PlateRectangle.Left + r1.PlateRectangle.Width / 2;
        //            var y1 = r1.PlateRectangle.Top + r1.PlateRectangle.Height / 2;
        //            var x2 = r2.PlateRectangle.Left + r2.PlateRectangle.Width / 2;
        //            var y2 = r2.PlateRectangle.Top + r2.PlateRectangle.Height / 2;
        //            var timespan = plateNumbers2.Frame.CaptureTime - plateNumbers1.Frame.CaptureTime;

        //            var speed = CalcSpeed(x1, y1, x2, y2, timespan.Milliseconds * 1000);

        //            var arg = new LprCaptured();
        //            arg.CaptureTime = plateNumbers1.Frame.CaptureTime;
        //            arg.JpegBuffers = new List<byte[]>(2);
        //            arg.JpegBuffers.Add(plateNumbers1.Frame.JpegBuffer);
        //            arg.JpegBuffers.Add(plateNumbers2.Frame.JpegBuffer);
        //            arg.Speed = speed;
        //            arg.LprNumber = r1.PlateNumber;


        //            PrintAndSaveImage(arg);

        //            RaiseNewLprCaptured(new EventArgs<LprCaptured>(arg));
        //        }


        //        //无法计算的车牌仅仅输出
        //        var single1 = plateNumbers1.Result.Where(p => !commonPlates.Contains(p)).ToArray();
        //        if (commonPlates.Count == 0)
        //        {
        //            var rr1 = new RecogResult();
        //            rr1.Frame = plateNumbers1.Frame;
        //            rr1.Result = single1.ToArray();
        //            singlePicResults.Add(rr1);
        //        }


        //        var single2 = plateNumbers2.Result.Where(p => !commonPlates.Contains(p)).ToArray();
        //        if (commonPlates.Count == 0)
        //        {
        //            var rr2 = new RecogResult();
        //            rr2.Frame = plateNumbers2.Frame;
        //            rr2.Result = single2.ToArray();
        //            singlePicResults.Add(rr2);
        //        }
        //    }
        //    else
        //    {
        //        singlePicResults.AddRange(res);
        //    }


        //    foreach (var singlePicResult in singlePicResults)
        //    {

        //        if (singlePicResult.Result.Any(r => r.Succeed))
        //        {
        //            foreach (var r in singlePicResult.Result.Where(r => r.Succeed))
        //            {
        //                var arg = new LprCaptured();
        //                arg.CaptureTime = singlePicResult.Frame.CaptureTime;
        //                arg.JpegBuffers = new List<byte[]>(1);
        //                arg.JpegBuffers.Add(singlePicResult.Frame.JpegBuffer);
        //                arg.LprNumber = r.PlateNumber;
        //                PrintAndSaveImage(arg);
        //                RaiseNewLprCaptured(new EventArgs<LprCaptured>(arg));
        //            }
        //        }
        //        else
        //        {
        //            var arg = new LprCaptured();
        //            arg.CaptureTime = singlePicResult.Frame.CaptureTime;
        //            arg.JpegBuffers = new List<byte[]>(1);
        //            arg.JpegBuffers.Add(singlePicResult.Frame.JpegBuffer);
        //            PrintAndSaveImage(arg);
        //            RaiseNewLprCaptured(new EventArgs<LprCaptured>(arg));
        //        }
        //    }

        //    _frames.Clear();
        //}

        private void PrintAndSaveImage(LprCaptured lprCaptured)
        {
            if (SaveImage)
            {
                for (int l = 0; l < lprCaptured.JpegBuffers.Count; l++)
                {
                    var msg = string.Format("抓拍时间：{0} 车牌号码：{1} 车速：{2} Km/h", lprCaptured.CaptureTime, lprCaptured.LprNumber ?? "未识别",
                    lprCaptured.Speed.HasValue ? lprCaptured.Speed.Value : 0);

                    var id = _gdPictureImaging.CreateGdPictureImageFromStream(new MemoryStream(lprCaptured.JpegBuffers[l]));

                    _gdPictureImaging.DrawTextBackColor(id, msg, 0, 0, 20, FontStyle.FontStyleRegular, Color.Yellow, Color.FromArgb(127, Color.Black), "Tahoma", true);
                    var absolutePath = Helper.GeneratePath(FilePathFormatString, _targetDir, lprCaptured, l);
                    var dir = Path.GetDirectoryName(absolutePath);
                    if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }


                    _gdPictureImaging.SaveAsJPEG(id, absolutePath, 95);

                    _gdPictureImaging.ReleaseGdPictureImage(id);
                }


            }
        }


        private string GenerateDefaultPathFormat()
        {
            return "{0:d4}{1:d2}{2:d2}/{3:d2}{4:d2}{5:d2}-{8}-{9}-{13}";
        }



        private int? CalcSpeed(int x1, int y1, int x2, int y2, int nanosecond)
        {
            var calculator = SpeedCalculator;
            if (calculator == null)
            {
                return null;
            }

            return calculator.CalcSpeed(x1, y1, x2, y2, nanosecond);
        }


        private LprWrapperSharp.RecognizeResult RecognizeOneImage(Image image)
        {
            var roi = new Rectangle(0, image.Height / 2, image.Width, image.Height / 2);
            return _recognizer.RecogImage((Bitmap)image, image.Width, image.Height, roi);
        }

        private RecogResult RecogniseOneFrame(Frame frame)
        {
            var recogResults = new List<LprWrapperSharp.RecognizeResult>();
            var img = new Bitmap(100, 200);// Image.FromStream(new MemoryStream(frame.JpegBuffer));

            if (Rois == null)
            {
                var r = new Rectangle(0, img.Height / 2, (int)(img.Width * 0.75), img.Height / 2);
                var res1 = _recognizer.RecogImage((Bitmap)img, img.Width, img.Height, r);


                r.Offset((int)(img.Width * 0.25), 0);
                var res2 = _recognizer.RecogImage((Bitmap)img, img.Width, img.Height, r);

                if (res1.Succeed != res2.Succeed)
                {
                    recogResults.Add(res1.Succeed ? res1 : res2);
                }
                else
                {
                    recogResults.AddRange(new[] { res1, res2 });
                }
            }
            else
            {
                foreach (var r in Rois)
                {
                    var res = _recognizer.RecogImage((Bitmap)img, r.Width, r.Height);
                    recogResults.Add(res);
                }
            }

            img.Dispose();

            var retValue = new RecogResult();
            retValue.Frame = frame;
            retValue.Result = recogResults.Distinct(new RecogResultComparer()).ToArray(); //去除重复车牌
            return retValue;
        }

        private bool IsNewGroupBegin(Frame value)
        {
            if (_frames.Count > 0)
            {
                var f = _frames.Last();
                if (f.PlateNumbers.Length != value.PlateNumbers.Length)
                {
                    return true;
                }
                else
                {
                    var res = IsSamePlateNumberGroup(f.PlateNumbers, value.PlateNumbers);
                    return !res;
                }
            }

            return false;
        }

        private bool IsSamePlateNumberGroup(IEnumerable<string> plateNumbers1, IEnumerable<string> plateNumbers2)
        {
            return plateNumbers1.All(plateNumbers2.Contains);
        }

        public void Stop()
        {
            StopAllEndPoint();

        }



    }
}
